package cn.flying.cloud.base.mybatis.handler;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.Objects;

import org.apache.ibatis.reflection.MetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;

import cn.flying.cloud.base.common.constants.Constants;
import cn.flying.cloud.base.common.enums.DeletedEnum;
import cn.flying.cloud.base.common.utils.lang.SnowflakeUtil;
import cn.flying.cloud.base.common.utils.lang.ThreadLocalUtil;

/**
 * mybatis plus 自动填充字段
 *
 * @author: admin
 * @date: 2024年02月04日 21:48
 * @version: 1.0
 */
@Component
public class AutoFillMetaObjectHandler implements MetaObjectHandler {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void insertFill(MetaObject metaObject) {
        if (logger.isDebugEnabled()) {
            logger.debug("start insert fill ....");
        }
        Object username = ThreadLocalUtil.get(Constants.USER_NAME);
        if (username == null) {
            username = "system";
        }
        // 起始版本 3.3.0(推荐使用)
        // 主键ID
        if (!isIdTypeAssignId(metaObject)) {
            // 检查id字段是否为null（strictInsertFill的核心逻辑）
            Object idValue = metaObject.getValue("id");
            if (idValue == null) {
                this.strictInsertFill(metaObject, "id", Long.class, SnowflakeUtil.nextId());
            }
        }

        this.strictInsertFill(metaObject, "createdTime", () -> LocalDateTime.now(), LocalDateTime.class);
        // 或者
//        this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
        if (Objects.isNull(metaObject.getValue("createdBy"))) {
            metaObject.setValue("createdBy", username);
        }
        this.strictInsertFill(metaObject, "updatedTime", () -> LocalDateTime.now(), LocalDateTime.class);

        if (Objects.isNull(metaObject.getValue("updatedBy"))) {
            metaObject.setValue("updatedBy", username);
        }
        Object tenantId = ThreadLocalUtil.get(Constants.TENANT_ID);
        if (Objects.isNull(metaObject.getValue("tenantId")) && Objects.nonNull(tenantId)) {
            metaObject.setValue("tenantId", tenantId);
        }
        // 逻辑删除标识
        this.strictInsertFill(metaObject, "deleted", String.class, DeletedEnum.N.getCode());
        // 乐观锁版本号
        this.strictInsertFill(metaObject, "revision", Long.class, 0L);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        if (logger.isDebugEnabled()) {
            logger.debug("start update fill ....");
        }
        Object username = ThreadLocalUtil.get(Constants.USER_NAME);
        if (username == null) {
            username = "system";
        }
        // 起始版本 3.3.3(推荐)
        this.strictUpdateFill(metaObject, "updatedTime", () -> LocalDateTime.now(), LocalDateTime.class);

        if (Objects.isNull(metaObject.getValue("updatedBy"))) {
            metaObject.setValue("updatedBy", username);
        }
    }

    /**
     * 判断实体类的主键是否配置了IdType.ASSIGN_ID
     */
    private boolean isIdTypeAssignId(MetaObject metaObject) {
        try {
            // 获取实体类的Class对象
            Class<?> entityClass = metaObject.getOriginalObject().getClass();
            // 获取实体类中标记了@TableId的字段
            Field[] fields = entityClass.getDeclaredFields();
            for (Field field : fields) {
                TableId tableId = field.getAnnotation(TableId.class);
                if (tableId != null) {
                    // 检查主键策略是否为ASSIGN_ID
                    return tableId.type() == IdType.ASSIGN_ID;
                }
            }
        } catch (Exception e) {
            // 反射获取字段失败时，默认不处理
            logger.warn("反射获取字段失败");
        }
        return false;
    }
}